using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Text.RegularExpressions;
using System.Windows.Forms;

namespace TripTracker
{
    class GSMModemConnector
    {

        private static string GSMInfo = null;

        [StructLayout(LayoutKind.Sequential)]
		public class CommTimeouts 
		{
			public UInt32 ReadIntervalTimeout;
			public UInt32 ReadTotalTimeoutMultiplier;
			public UInt32 ReadTotalTimeoutConstant;
			public UInt32 WriteTotalTimeoutMultiplier;
			public UInt32 WriteTotalTimeoutConstant;
		}

		[Flags]
			public enum CommEventFlags : uint
		{
			NONE        = 0x0000, //
			RXCHAR      = 0x0001, // Any Character received
			RXFLAG      = 0x0002, // Received specified flag character
			TXEMPTY     = 0x0004, // Tx buffer Empty
			CTS         = 0x0008, // CTS changed
			DSR         = 0x0010, // DSR changed
			RLSD        = 0x0020, // RLSD changed
			BREAK       = 0x0040, // BREAK received
			ERR         = 0x0080, // Line status error
			RING        = 0x0100, // ring detected
			PERR        = 0x0200, // printer error
			RX80FULL    = 0x0400, // rx buffer is at 80%
			EVENT1      = 0x0800, // provider event
			EVENT2      = 0x1000, // provider event
			POWER       = 0x2000, // wince power notification
			ALL			= 0x3FFF  // mask of all flags 
		}


		public const Int32 INVALID_HANDLE_VALUE = -1;
		public const UInt32 OPEN_EXISTING = 3;
		public const UInt32 GENERIC_READ = 0x80000000;
		public const UInt32 GENERIC_WRITE = 0x40000000;

		public const UInt32 SETRTS = 3;
		public const UInt32 CLRRTS = 4;
		public const UInt32 SETDTR = 5;
		public const UInt32 CLRDTR = 6;

		[DllImport("coredll.dll", EntryPoint="CreateFileW", SetLastError = true)]
		public static extern IntPtr CreateFileW(
			String lpFileName, UInt32 dwDesiredAccess, UInt32 dwShareMode,
			IntPtr lpSecurityAttributes, UInt32 dwCreationDisposition, UInt32 dwFlagsAndAttributes,
			IntPtr hTemplateFile);

		[DllImport("coredll.dll", EntryPoint="CloseHandle", SetLastError = true)]
		public static extern int CloseHandle(IntPtr hObject);

		[DllImport("coredll.dll", EntryPoint="GetCommState", SetLastError = true)]
		public static extern int GetCommState(IntPtr hFile, DCB dcb);

		[DllImport("coredll.dll", EntryPoint="SetCommState", SetLastError = true)]
		public static extern int SetCommState(IntPtr hFile, DCB dcb);
		
		[DllImport("coredll.dll", EntryPoint="SetCommTimeouts", SetLastError = true)]
		public static extern int SetCommTimeouts(IntPtr hFile, CommTimeouts timeouts);

		[DllImport("coredll.dll", EntryPoint="GetCommTimeouts", SetLastError = true)]
		public static extern int GetCommTimeouts(IntPtr hFile, CommTimeouts timeouts);

		[DllImport("coredll.dll", EntryPoint="EscapeCommFunction", SetLastError = true)]
		public static extern int EscapeCommFunction(IntPtr hFile, UInt32 dwFunc);
				
		[DllImport("coredll.dll", SetLastError = true)]
		public static extern int DeviceIoControl( IntPtr hDevice, uint dwIoControlCode, byte[] lpInBuffer, UInt32 nInBufferSize, out byte[] lpOutBuffer, Int32 nOutBufferSize, ref Int32 lpBytesReturned, IntPtr lpOverlapped);
		
		[DllImport("coredll.dll", EntryPoint="WriteFile", SetLastError = true)]
		public static extern int WriteFile(IntPtr hFile, byte[] lpBuffer, UInt32 nNumberOfBytesToWrite, ref Int32 lpNumberOfBytesWritten, IntPtr lpOverlapped);

		[DllImport("coredll.dll", EntryPoint="ReadFile", SetLastError = true)]
		public static extern int ReadFile(IntPtr hFile, byte[] lpBuffer, UInt32 nNumberOfBytesToRead, ref Int32 lpNumberOfBytesRead, IntPtr lpOverlapped);

		[DllImport("coredll.dll", EntryPoint="WaitCommEvent", SetLastError = true)]
		public static extern int WaitCommEvent(IntPtr hFile, ref CommEventFlags lpEvtMask, IntPtr lpOverlapped);

        public GSMModemConnector()
		{
		}

		public static void SendModemCommand( string port, string modemCommand )
		{

			uint access = GENERIC_WRITE | GENERIC_READ;
			byte[] ioctlcommand = { 0x84, 0x00 };
			byte[] comdevoutput = new byte[256];
			byte[] comdevinput = new byte[256];
			byte[] command = toByteArray( modemCommand );


            IntPtr hPort = CreateFileW(port, access, 0, IntPtr.Zero, GSMModemConnector.OPEN_EXISTING, 0, IntPtr.Zero);

			DCB dcb = new DCB();

			CommTimeouts comm = new CommTimeouts();			
			CommEventFlags eventFlags = new CommEventFlags();

			int bytesReturned = 0;
			int bytesSent = 0;
			
			if ( hPort != (IntPtr)INVALID_HANDLE_VALUE )
			{
				try
				{

					GetCommState( hPort, dcb );

					dcb.BaudRate = (uint)115200;
					dcb.ByteSize = (byte)8;
					dcb.fParity = false;
					dcb.StopBits = 0;

					SetCommState( hPort, dcb );

                    EscapeCommFunction(hPort, GSMModemConnector.SETDTR);
                    EscapeCommFunction(hPort, GSMModemConnector.SETRTS);

					GetCommTimeouts( hPort, comm );

					comm.ReadIntervalTimeout = 100;
					comm.ReadTotalTimeoutConstant = 200;
					comm.ReadTotalTimeoutMultiplier = 20;
					comm.WriteTotalTimeoutConstant = 1000;
					comm.WriteTotalTimeoutMultiplier = 20;

                    GSMModemConnector.SetCommTimeouts(hPort, comm);

					//DeviceIoControl( hPort, 0xAAAA5679, ioctlcommand, (uint)ioctlcommand.Length, out comdevoutput, comdevoutput.Length, ref bytesReturned, IntPtr.Zero );
								
					WriteFile( hPort, command, (uint)command.Length, ref bytesSent, IntPtr.Zero );

					WaitCommEvent( hPort, ref eventFlags, IntPtr.Zero );

					ReadFile( hPort, comdevinput, (uint)comdevinput.Length, ref bytesReturned, IntPtr.Zero );

                    EscapeCommFunction(hPort, GSMModemConnector.CLRDTR);

				}
				finally
				{
					CloseHandle( hPort );
				}

			}

            GSMInfo = toString(comdevinput, bytesReturned);
		}


		public static void SendModemCommand( string port, string modemPreCommand, string modemCommand  )
		{
			uint access = GENERIC_WRITE | GENERIC_READ;
			byte[] ioctlcommand = { 0x84, 0x00 };
			byte[] comdevoutput = new byte[256];			
			byte[] comdevinput = new byte[256];
			byte[] precommand = toByteArray( modemPreCommand );
			byte[] command = toByteArray( modemCommand );

            IntPtr hPort = CreateFileW(port, access, 0, IntPtr.Zero, GSMModemConnector.OPEN_EXISTING, 0, IntPtr.Zero);
			
			DCB dcb = new DCB();
			CommTimeouts comm = new CommTimeouts();			
			CommEventFlags eventFlags = new CommEventFlags();

			int bytesReturned = 0;
			int bytesSent = 0;
			
			if ( hPort != (IntPtr)INVALID_HANDLE_VALUE )
			{				
				try
				{
					GetCommState( hPort, dcb );
  
					dcb.BaudRate = (uint)115200;
					dcb.ByteSize = (byte)8;
					dcb.fParity = false;
					dcb.StopBits = 0;

					SetCommState( hPort, dcb );

                    EscapeCommFunction(hPort, GSMModemConnector.SETDTR);
                    EscapeCommFunction(hPort, GSMModemConnector.SETRTS);

					GetCommTimeouts( hPort, comm );

					comm.ReadIntervalTimeout = 100;
					comm.ReadTotalTimeoutConstant = 200;
					comm.ReadTotalTimeoutMultiplier = 20;
					comm.WriteTotalTimeoutConstant = 1000;
					comm.WriteTotalTimeoutMultiplier = 20;

                    GSMModemConnector.SetCommTimeouts(hPort, comm);

                    //--------
					//DeviceIoControl( hPort, 0xAAAA5679, ioctlcommand, (uint)ioctlcommand.Length, out comdevoutput, comdevoutput.Length, ref bytesReturned, IntPtr.Zero );
	
					WriteFile( hPort, precommand, (uint)precommand.Length, ref bytesSent, IntPtr.Zero );

					WaitCommEvent( hPort, ref eventFlags, IntPtr.Zero );
				
					ReadFile( hPort, comdevinput, (uint)comdevinput.Length, ref bytesReturned, IntPtr.Zero );				

					WriteFile( hPort, command, (uint)command.Length, ref bytesSent, IntPtr.Zero );
					WaitCommEvent( hPort, ref eventFlags, IntPtr.Zero );
				
					ReadFile( hPort, comdevinput, (uint)comdevinput.Length, ref bytesReturned, IntPtr.Zero );

                    EscapeCommFunction(hPort, GSMModemConnector.CLRDTR);
				}
				finally
				{
					CloseHandle( hPort );
				}

			}

			GSMInfo = toString( comdevinput, bytesReturned );
		}

		private static byte[] toByteArray( string s )
		{
			byte[] outarr = new byte[s.Length+1];
			int i=0;
			foreach ( char c in s )
			{
				outarr[i] = (byte)c;
				i++;
			}
			return outarr;
		}

		private static string toString( byte[] inarr, int bytesReturned )
		{
			string outstring = "";
			
			if ( bytesReturned >= 0 )
			{				
				for ( int i=0; i<bytesReturned; i++ )
				{
					outstring += (char)inarr[i];
				}
			}
			return outstring;
            //+CREG: 2,1, "4E5d", "4Dc5"
            //0
		}

        private static void refresh() {
            GSMModemConnector.SendModemCommand("COM9:", "AT+CREG=2\r", "AT+CREG?\r");
        }

        private static String getGSMInfo(int index) {
            refresh();
            Regex regex1 = new Regex("\"([a-zA-Z0-9]*)\"");
            Match match = regex1.Match( GSMInfo );
            int i = 1;
            while(match.Success) {
                if (i == index)
                    return match.Value.Replace('"', ' ').Trim();
                match = match.NextMatch();
                i++;
            }
            return "";
        }

        public static String getLAI()
        {
            return getGSMInfo(1);
        }

        public static String getCellID()
        {
            return getGSMInfo(2);
        }

	}

    }
